use std::path::PathBuf;

use clap::Parser;
use wtask_app::{App, cli_app, cli_appres, msg_func_print};
use wtask_base::{error::{WError, WResult}, export::{ctrl_c, kill_channel, log_error, log_info}, logs::LogArgs, machine::{WTASK_CLI_CONFIG_FILE, WTASK_MACHINE_CONFIG_FILE, load_config_from_file, runtime_init}};




#[derive(Debug, Parser, Clone)]
pub struct CMDSubRun {
    #[command(subcommand)]
    data: App,

    /// 使用指定配置文件，或者local表示连接本地machine运行任务，使用数字表示指定使用第几个machine配置
    #[arg(long)]
    config: Option<String>,

    #[command(flatten)]
    pub log_args: LogArgs,
}


impl CMDSubRun {
    pub fn run(self) -> WResult<()> {
        let config_all = match self.config {
            Some(v) if v.eq("local") => load_config_from_file(WTASK_MACHINE_CONFIG_FILE, None)?,
            Some(v) => {
                let config_all = load_config_from_file(WTASK_CLI_CONFIG_FILE, None)?;
                let mut n = 0;
                if let Ok(t) = v.parse::<usize>()
                    && t > 0 && t <= config_all.len() {
                    n = t;
                }
                if n == 0 {
                    let f = PathBuf::from(v);
                    let dir = f.parent().unwrap().to_str();
                    let file = f.file_name().unwrap().to_str().unwrap();
                    load_config_from_file(file, dir)?
                } else {
                    vec![config_all[n-1].clone()]
                }
            },
            None => load_config_from_file(WTASK_CLI_CONFIG_FILE, None)?
        };
        match (config_all.len(), &self.data) {
            (n, App::Shell(_) | App::Port(_) |App::Room(_) | App::FileDownload(_) | App::Port(_)) if n > 1 => {
                log_error!("Please use arg config to one machine");
                return Ok(());
            },
            (0, _) => {
                log_error!("No machine found");
                return Ok(());
            },
            _ => {}
        };
        // run
        match self.data {
            App::Port(app_port) => {
                let runtime = runtime_init(4)?;
                let (tx, rx) = kill_channel(false);
                let h = runtime.spawn(async move {
                    log_info!("Ctrl-C Stop PortForwarding");
                    let _ = ctrl_c().await;
                    let _ = tx.send(true);
                });
                runtime.block_on(async move {
                    let _ = cli_app(app_port, &config_all[0], Some((rx, config_all[0].clone())), msg_func_print).await;
                });
                runtime.block_on(h).unwrap();
                runtime.shutdown_background();
            },
            app => {
                let app_req = app.client_handle_before(msg_func_print)?;
                if app_req.is_none() {
                    return Ok(())
                }
                let app = app_req.unwrap();
                let runtime = runtime_init(4)?;
                let hs = config_all.into_iter().map(| config | {
                    let app_clone = app.clone();
                    runtime.spawn(async move {
                        log_info!("Machine {} start ...", config);
                        match cli_appres(app_clone, &config, msg_func_print).await {
                            Ok(_) => {},
                            Err(WError::NetError(v)) if v == "Channel close" => {}
                            Err(e) => {
                                log_error!("{} {}", config, e);
                            }
                        };
                        log_info!("Machine {} end", config);
                    })
                }).collect::<Vec<_>>();
                for handle in hs {
                    runtime.block_on(handle).unwrap();
                }
                runtime.shutdown_background();
            }
        }
        Ok(())
    }
}